package loveWaves.compatibility.wtp;

import loveWaves.compatibility.CompatibilityServiceInterface;

import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.io.StringReader;
import java.net.MalformedURLException;
import java.net.URL;
import java.nio.CharBuffer;
import java.util.*;

import com.hp.hpl.jena.ontology.OntClass;
import com.hp.hpl.jena.ontology.OntModel;
import com.hp.hpl.jena.ontology.OntModelSpec;
import com.hp.hpl.jena.ontology.OntProperty;
import com.hp.hpl.jena.ontology.OntResource;
import com.hp.hpl.jena.rdf.model.Literal;
import com.hp.hpl.jena.rdf.model.Model;
import com.hp.hpl.jena.rdf.model.ModelFactory;
import com.hp.hpl.jena.rdf.model.NodeIterator;
import com.hp.hpl.jena.rdf.model.Property;
import com.hp.hpl.jena.rdf.model.RDFVisitor;
import com.hp.hpl.jena.rdf.model.Resource;
import com.hp.hpl.jena.rdf.model.StmtIterator;
import com.hp.hpl.jena.util.iterator.ExtendedIterator;
import com.hp.hpl.jena.vocabulary.RDF;

public class CompatibilityService implements CompatibilityServiceInterface {
	
	public Map<Long,Model> profiles;
	
	private OntModel ontModel;
	private OntProperty age;
	private OntProperty ageMin;
	private OntProperty ageMax;
	private OntProperty music;
	private OntProperty movie;
	private OntProperty cuisine;
	private OntProperty movieTitle;
	private OntProperty musicTitle;
	
	private OntResource requesterDater;
	private OntResource requestedDater;
	private Model requesterModel;
	private Model requestedModel;
	
	private OntClass male ;
	private OntClass female;
	
	private OntClass dater;
	
	private static final String ONTMODELLOCATION = "file:///Users/julien/Documents/workspace/loveWaves/ontology/loveWavesRDF.rdfs";
	
	public CompatibilityService(){
		profiles = new HashMap<Long,Model>();
		ontModel = ModelFactory.createOntologyModel(OntModelSpec.RDFS_MEM_RDFS_INF);
		ontModel.read(ONTMODELLOCATION);
		ageMin = ontModel.getOntProperty("http://purl.org/NET/S3MW3BTeam/lw#lookForAgeMin");
		ageMax = ontModel.getOntProperty("http://purl.org/NET/S3MW3BTeam/lw#lookForAgeMax");
		age = ontModel.getOntProperty("http://purl.org/NET/S3MW3BTeam/lw#age");
		music = ontModel.getOntProperty("http://purl.org/NET/S3MW3BTeam/lw#music");
		movie = ontModel.getOntProperty("http://purl.org/NET/S3MW3BTeam/lw#movies");
		cuisine = ontModel.getOntProperty("http://purl.org/NET/S3MW3BTeam/lw#favouriteCuisine");
		movieTitle = ontModel.getOntProperty("http://purl.org/NET/S3MW3BTeam/lw#movieTitle");
		musicTitle = ontModel.getOntProperty("http://purl.org/NET/S3MW3BTeam/lw#musicTitle");
		
		
		dater = ontModel.getOntClass("http://purl.org/NET/S3MW3BTeam/lw#Dater");
		
		male = ontModel.getOntClass("http://purl.org/NET/S3MW3BTeam/lw#Male");
		female = ontModel.getOntClass("http://purl.org/NET/S3MW3BTeam/lw#Female");
	}
	
	/**
	 * @deprecated
	 */
	public int getCompatibilityScore(String requesterRDFXML, long requestedRFID) {
		// TODO Auto-generated method stub
		return 0;
	}

	/**
	 * @deprecated
	 */
	public String getProfile(String requesterRDFXML, long requestedRFID,
			int informationQuantity) {
		// TODO Auto-generated method stub
		return null;
	}

	public void registerProfile(String profileRDFXML, long RFID) {
		Model model = ModelFactory.createModel(OntModelSpec.RDFS_MEM_RDFS_INF);
		model.read((new StringReader(profileRDFXML)),"UTF-8");
		profiles.put(RFID, model);
	}

	public int getCompatibilityScore(long requesterRFID, long requestedRFID) {
		getDaterResources(requesterRFID, requestedRFID);
		
		int compatibility = 0;
		/* age is important. +2 if compatible */
		if(ageOK(requesterDater, requestedDater)){
			compatibility+=2;
			System.out.println("AGE MATCH!");
		}
		/* music */
		if(commonStuff(requesterDater, requestedDater, music).size() != 0){
			compatibility++;
			System.out.println("MUSIC MATCH!");
		}
		/* movies */
		if(commonStuff(requesterDater, requestedDater, movie).size() != 0){
			compatibility++;
			System.out.println("MOVIE MATCH!");
		}
		/* cuisine */
		if(commonStuff(requesterDater, requestedDater, cuisine).size() != 0){
			compatibility++;
			System.out.println("CUISINE MATCH!");
		}
		/* make it clean */
		unregisterDaterResources();
		return compatibility;
	}

	public String getProfile(long requesterRFID, long requestedRFID, int informationQuantity) {
		getDaterResources(requesterRFID, requestedRFID);
		boolean isMale = false;
		String nick;
		String rdfToReturn ="<?xml version='1.0' encoding='UTF-8'?><!DOCTYPE rdf:RDF [<!ENTITY rdf 'http://www.w3.org/1999/02/22-rdf-syntax-ns#'><!ENTITY lw 'http://purl.org/NET/S3MW3BTeam/lw#'>"+
		"<!ENTITY lwProf 'http://purl.org/NET/S3MW3BTeam/lw/profiles#'>"+
		"<!ENTITY foaf 'http://xmlns.com/foaf/0.1/'>"+
		"]>"+
		"<rdf:RDF xmlns:lw=\"&lw;\""+
		    " xmlns:lwProf=\"&lwProf;\""+
		    " xmlns:rdf=\"&rdf;\""+
		    " xmlns:foaf=\"&foaf;\">";
		NodeIterator nodeIter = requestedDater.listPropertyValues(RDF.type);
		/* Checks the gender of the dater */
		while(nodeIter.hasNext()){
			Resource res = (Resource)(nodeIter.next());
			if((res).equals((Resource)male)){
				isMale = true;
			}
		}
		Property nickProp = ontModel.createProperty("http://xmlns.com/foaf/0.1/nick");
		Literal litt = (Literal)(requestedDater.getPropertyValue(nickProp));
		nick = litt.getString();
		if(isMale){
			rdfToReturn +="<lw:Male ";
		} else {
			rdfToReturn +="<lw:Female ";
		}
		rdfToReturn += "rdf:about=\""+requestedDater.getURI()+"\" ";
		rdfToReturn += "foaf:nick=\""+nick+"\">";
		boolean ageOK;
		Set<OntResource> commonMusic;
		Set<OntResource> commonMovies;
		Set<OntResource> commonCuisine;
		ageOK = ageOK(requesterDater, requestedDater);
		commonMusic = commonStuff(requesterDater, requestedDater, music);
		commonMovies = commonStuff(requesterDater, requestedDater, movie);
		commonCuisine = commonStuff(requesterDater, requestedDater, cuisine);
		Integer informationNumber =Math.min(informationQuantity, 1+commonMusic.size()+commonMovies.size()+commonCuisine.size());
		/* This is where we should implement the user's priorities */
		/* Here it is age, music movies then cuisine by default */
		/*while(informationNumber != 0){
			
		}*/
			if(ageOK){
				rdfToReturn += getRDFAge(informationNumber, requestedDater);
			}
			System.out.println("info "+informationNumber);
			//System.out.println(commonMusic);
			Iterator<OntResource> musicIter = commonMusic.iterator();
			while(musicIter.hasNext()){
				System.out.println("pouet");
				String title="";
				OntResource currentMusic = musicIter.next();
				NodeIterator iterInter = currentMusic.listPropertyValues(nickProp);
				rdfToReturn+="<lw:music><lw:Music "+"" +
					"rdf:about=\""+currentMusic.getURI()+"\" "+
					//"lw:musicTitle=\""+currentMusic.getProperty(musicTitle)+"\""+
					"/></lw:music>";
				informationNumber--;
				if(informationNumber == 0){break;}
			}	
			Iterator<OntResource> movieIter = commonMovies.iterator();
			while(movieIter.hasNext()){
				OntResource currentMovie = movieIter.next();
				rdfToReturn+="<lw:movies><lw:Movie "+"" +
					"rdf:about=\""+currentMovie.getURI()+"\" "+
					//"lw:movieTitle=\""+currentMovie.getProperty(movieTitle)/*.getString()*/+"\""+
					"/></lw:movies>";
				informationNumber--;
			}	
			Iterator<OntResource> cuisineIter = commonCuisine.iterator();
			while(cuisineIter.hasNext()){
				OntResource currentCuisine = cuisineIter.next();
				rdfToReturn+="<lw:favouriteCuisine "+
				"rdf:resource=\""+currentCuisine.getURI()+"\"/>";
				informationNumber--;
			}
		if(isMale){
			rdfToReturn +="</lw:Male>";
		} else {
			rdfToReturn += "</lw:Female>";
		}
		rdfToReturn+="</rdf:RDF>";
		unregisterDaterResources();
		return rdfToReturn;
	}
	
	private String getRDFAge(int informationNumber, OntResource requestedDater){
		String toReturn="";
		toReturn += "<lw:age>"+requestedDater.getPropertyValue(age)+"</lw:age>";
		informationNumber --;
		return toReturn;
	}
	
	private void getDaterResources(Long requesterRFID, Long requestedRFID){
		//bugs if we ask for the same dater
		requesterModel = profiles.get(requesterRFID);
		requestedModel = profiles.get(requestedRFID);
		ontModel.addSubModel(requesterModel);
		ontModel.addSubModel(requestedModel);
		ExtendedIterator iterDater = dater.listInstances();
		requesterDater = (OntResource)iterDater.next();
		requestedDater = (OntResource)iterDater.next();
	}
	
	private void unregisterDaterResources(){
		ontModel.removeSubModel(requesterModel);
		ontModel.removeSubModel(requestedModel);
		requesterDater = null;
		requestedDater = null;
		requesterModel = null;
		requestedModel = null;
	}
	
	private boolean ageOK(OntResource requester,OntResource requested){
		//TODO check age problem
		int ageMinRequester = Integer.parseInt((requester.getPropertyValue(ageMin)).toString());
		int ageMaxRequester = Integer.parseInt((requester.getPropertyValue(ageMax)).toString());
		int ageRequested = Integer.parseInt((requested.getPropertyValue(age)).toString());
		//System.out.println(ageMinRequester+" "+ageMaxRequester+" "+ageRequested);
		return ((ageRequested >= ageMinRequester) && (ageRequested <= ageMaxRequester));
	}
	
	private Set<OntResource> commonStuff(OntResource requester,OntResource requested,OntProperty prop){
		Set<OntResource> requestedStuff = new HashSet<OntResource>();
		Set<OntResource> requesterStuff = new HashSet<OntResource>();
		
		NodeIterator requestedNodeIter = requested.listPropertyValues(prop);
		while(requestedNodeIter.hasNext()){
			OntResource res = ontModel.getOntResource((Resource)requestedNodeIter.next());
			requestedStuff.add(res);
		}
		
		NodeIterator requesterNodeIter = requester.listPropertyValues(prop);
		while(requesterNodeIter.hasNext()){
			Resource reso = (Resource)requesterNodeIter.next();
			//System.out.println(reso);
			OntResource res = ontModel.getOntResource(reso);
			requesterStuff.add(res);
		}
		//System.out.println(requesterStuff);
		//System.out.println(requestedStuff);
		requestedStuff.retainAll(requesterStuff);
		
		return requestedStuff;
	}
	
	public static void main(String[] args) throws Exception{
		test();
	}
	
	public static void test() throws Exception{
		CompatibilityService service = new CompatibilityService();
		long id1 = 782087234;
		//OntModel pouetMod = ModelFactory.createOntologyModel(OntModelSpec.RDFS_MEM_RDFS_INF);
		//pouetMod.read("file:///Users/julien/Documents/workspace/loveWaves/testProfiles/Anna.rdf");
		//pouetMod.listStatements();
		//BufferedWriter writer = new BufferedWriter(new PrintWriter(System.out));
		String prof = "<?xml version='1.0' encoding='UTF-8'?><!DOCTYPE rdf:RDF [<!ENTITY rdf 'http://www.w3.org/1999/02/22-rdf-syntax-ns#'><!ENTITY lw 'http://purl.org/NET/S3MW3BTeam/lw#'>"+
"<!ENTITY lwProf 'http://purl.org/NET/S3MW3BTeam/lw/profiles#'>"+
"<!ENTITY foaf 'http://xmlns.com/foaf/0.1/'>"+
"]>"+
"<rdf:RDF xmlns:lw=\"&lw;\""+
    " xmlns:lwProf=\"&lwProf;\""+
    " xmlns:rdf=\"&rdf;\""+
    " xmlns:foaf=\"&foaf;\">"+
    "<lw:Female rdf:about=\"&lwProf;anna\""+ 
        " foaf:nick=\"Anna\" "+
        " lw:bloodType=\"A\""+
        " lw:drinkingHabits=\"occasionaly\""+
        " lw:age=\"25\""+
        ">"+
        "<lw:behaviour>Calm</lw:behaviour>"+
        "<lw:clothesStyle>Casual</lw:clothesStyle>"+
        "<lw:children rdf:resource=\"&lw;wantSomeChidren\"/>"+
        "<lw:ethnicity rdf:resource=\"&lw;Caucasian\" />"+
        "<lw:isCitizenOf rdf:resource=\"&lw;Poland\"/>"+
        "<lw:speaks rdf:resource=\"&lw;Polish\" />"+
        "<lw:speaks rdf:resource=\"&lw;German\" />"+
        "<lw:music>"+
        "	<lw:Music rdf:about=\"http://www.last.fm/music/Cock_And_Ball_Torture\" lw:musicTitle=\"Cock and Ball Torture\"/>"+
        "</lw:music>"+
        "<lw:music>"+
        "	<lw:Music rdf:about=\"http://www.last.fm/music/Libido_Airbag\"/>"+
        "</lw:music>"+
        "<lw:movies>"+
        "	<lw:Movie rdf:about=\"http://imdb.com/title/tt0076759/\" lw:movieTitle=\"Star Wars\"/>"+
        "</lw:movies>"+
        "<lw:movies>	"+
        "	<lw:Movie rdf:about=\"http://imdb.com/title/tt0066921/\" lw:movieTitle=\"A Clockwork Orange\"/>"+
        "</lw:movies>"+
        "<lw:aspect>Attractive</lw:aspect>"+
        "<lw:attractiveTraits>Hair</lw:attractiveTraits>"+
        "<lw:maritalStatus>Single</lw:maritalStatus>"+
        "<lw:favouriteCuisine rdf:resource=\"&lw;ItalianCuisine\"/>"+
        "<lw:goesOutIn rdf:resource=\"&lw;concert\"/>"+
        "<lw:hair rdf:resource=\"&lw;BlondeHair\"/>"+
        "<lw:hasChildren>0</lw:hasChildren>"+
        "<lw:height>169</lw:height>"+
        "<lw:hobbies>SM</lw:hobbies>"+
        "<lw:livesWith>With pets</lw:livesWith>"+
        "<lw:marriage>doesn't mind</lw:marriage>"+
        "<lw:occupation>Librarian</lw:occupation>"+
        "<lw:religion>Atheist</lw:religion>"+
        "<lw:studyLevel>BSc</lw:studyLevel>"+
        "<lw:weight>61</lw:weight>"+
        "<lw:lookForGender>female</lw:lookForGender>"+
        "<lw:lookForAgeMin>24</lw:lookForAgeMin>"+
        "<lw:lookForAgeMax>35</lw:lookForAgeMax>"+
    "</lw:Female>"+
"</rdf:RDF>";
		//System.out.println(prof);
		service.registerProfile(prof, id1);
		OntModel pouetMod = ModelFactory.createOntologyModel(OntModelSpec.RDFS_MEM_RDFS_INF);
		pouetMod.read("file:///Users/julien/Documents/workspace/loveWaves/testProfiles/Shaukat.rdf");
		long id2 =643782478;
		service.profiles.put(id2, pouetMod);
		OntModel pouetMod2 = ModelFactory.createOntologyModel(OntModelSpec.RDFS_MEM_RDFS_INF);
		long id3 = 1111111;
		pouetMod2.read("file:///Users/julien/Documents/workspace/loveWaves/testProfiles/Anna2.rdf");
		service.profiles.put(id3, pouetMod2);
		//System.out.println(service.profiles);
		//System.out.println(service.getCompatibilityScore(id1, id2));
		System.out.println(service.getProfile(id1, id2, 4));
		
	}

}
